// 附加函数组
import * as _php from './php.es6.js';
import * as _str from './string.es6.js';
import * as _num from './number.es6.js';
import * as _arr from './array.es6.js';
import * as _utils from './utils.es6.js';
import * as _redux from './redux.es6.js';
// export const num = _num;
// export const utils = _utils;
// export const str = _str;
// export const php = _php;
export const arr = _arr;
export const redux = _redux;

// 核心函数组


/**
 * 获取IE版号或edge
 */
export const ie = () => {
  let
    v = 3, div = document.createElement('div'),
    all = div.getElementsByTagName('i');
  while (
    div.innerHTML = `<!--[if gt IE ${++v}]><i></i><![endif]-->`,
    all[0]
  );
  v = v > 4 ? v : false;
  if(document.all && !v) v = 10;
  if('ActiveXObject' in window && !v) v = 11;
  if(window.navigator.appVersion.search('Edge') !== -1){
    v = 'edge';
  }
  return v;
}


/**
 * xmlhttp 请求
 * @param  {string} url     请求地址
 * @param  {object} options 设置
 * @return {emitter}
 */
export const xhr = (url, options = {}) => {
  let opts = assign({
      method: 'GET', // 请求方法
      type: 'json', // 内容类型
      cache: false,
      payload: false,
      formdata: false,
      withCredentials: false,
      showProgress: false,
      data: {},
      headers: {},
    }, options),
    xhr, progress = 0,
    send_data = [],
    emt = emitter(),
    has_q = url.split('/').pop().search(/\?/) !== -1;
  // 是否有缓存
  if (!opts.cache) {
    url += (has_q ? '&' : '?') + '_=' + Math.random();
    has_q = true;
  }
  // 整理发送数据
  if(opts.formdata){
    send_data = opts.data;
  }
  else if(serialize(opts.data) !== ''){
    send_data.push(serialize(opts.data));
  }
  // 如果是put /post 则用formdata
  if (/^put$|^post$/i.test(opts.method) && !opts.payload) {
    opts.headers['Content-type'] =
    'application/x-www-form-urlencoded; charset=UTF-8';
  } else if(send_data.length > 0) {
    if(!opts.formdata) url += (has_q ? '&' : '?') + send_data;
  }
  xhr = new XMLHttpRequest();
  xhr.open(opts.method, url, true);
  for (let k in opts.headers) {
    xhr.setRequestHeader(k, opts.headers[k]);
  }
  xhr.withCredentials = opts.withCredentials;
  // 如果支持进度条
  if(opts.showProgress){
    let progressFn = (e) => {
      if (e.lengthComputable) {
        progress = Math.round(e.loaded * 100 / e.total);
        emt.emit('progress', progress);
      }
    };
    if(xhr.upload){
      xhr.upload.addEventListener('progress', progressFn, false);
    }
    xhr.addEventListener('progress', progressFn, false);
  }
  xhr.addEventListener('load', e => {
    let res;
    if (e.target.status === 200 || e.target.status === 304) {
      res = e.target.responseText;
      if (opts.type === 'json') {
        try{
          res = JSON.parse(res);
        }
        catch(e){
          throw new Error('JSON数据解析失败');
        }
      }
      emt.emit('done', res);
    }
    else{
      emt.emit('error', e.target.status);
    }
  }, { once: true });

  xhr.addEventListener('error', (e) => {
    emt.emit('error', e.target.status);
  }, { once: true });

  xhr.addEventListener('loadend', () => {
    emt.emit('complete');
  }, { once: true });

  xhr.send(send_data);

  return emt;

};


/**
 * 资源加载器，按顺序加载，并行加载的放入一个数组中
 * @param  {Mixed} batches 文件组
 * @return {emitter}
 */
// 已经加载的文件名(作用：如果有重复的文件可以忽略加载)
let _PIP_FILELOADED = [];
export const load = (...batches) => {

  let
    emt = emitter(),
    // 开始索引
    index = 0,
    // 结束
    endIndex = batches.length || 0,
    // 支持的文件后缀
    fileSupport = {
      js: {
        tag: 'script',
        from: 'src',
        attrs: { defer: true }
      },
      css: {
        tag: 'link',
        from: 'href',
        attrs: { rel: 'stylesheet' }
      }
    },
    // 私有方法
    fn = {
      // 获取文件名
      fileName: url => {
        return url.split('/').pop().split('?')[0];
      },
      // 获取文件后缀
      fileExt: url => {
        return url.split('.').pop().split('?')[0];
      },
      fileLoad: url => {
        let
          emt2 = emitter(),
          ext = fn.fileExt(url),
          file, el;
        if(!ext in fileSupport){
          throw new Error(`${ext}格式文件不允许加载`);
        }
        file = fileSupport[ext];
        el = document.createElement(file.tag);
        el[file.from] = url;
        Object.keys(file.attrs).forEach(key => {
          el[key] = file.attrs[key];
        });
        el.addEventListener('load', () => {
          emt2.emit('done', url);
        }, false);
        el.addEventListener('error', () => {
          emt2.emit('error', url);
        }, false);
        document.head.appendChild(el);
        return emt2;
      },
      checker: (batchFileLoaded, n, emt) => {
        if(batchFileLoaded.length === n){
          index++;
          if(index === endIndex){
            setTimeout(function(){
              emt.emit('done', batchFileLoaded);
            }, 1);
            return;
          }
          fn.batchLoad(batches[index]);
        }
      },
      batchLoad: batch => {
        let
          n, // 组里有多少文件
          batchFileLoaded = []; // 组中已经成功加载的文件
        if(typeOf(batch) === 'String'){
          batch = [batch];
        }
        n = batch.length;
        batch.forEach(url => {
          if(_PIP_FILELOADED.indexOf(url.split('?')[0]) !== -1){
            // 有重复的就忽略
            batchFileLoaded.push(url.split('?')[0]);
            fn.checker(batchFileLoaded, n, emt);
            return;
          }
          fn.fileLoad(url)
          .on('done', file => {
            _PIP_FILELOADED.push(file.split('?')[0]);
            batchFileLoaded.push(url.split('?')[0]);
            fn.checker(batchFileLoaded, n, emt);
          })
          .on('error', file => {
            emt.emit('error', file);
          });
        });
      }
    };

  if(endIndex === 0){
    throw new Error('无资源加载');
  }

  fn.batchLoad(batches[index]);

  return emt;

};


/**
 * object => serialize
 * @param  {Object} obj
 * @return {String}
 */
export const serialize = (obj = {}) =>
  Object.keys(obj).map(k =>
    `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`
  ).join('&');


/**
 * hash search 转对象
 * ?a=a&b=b => {a:'a',b:'b'}
 * @param  {String} hash
 * @return {Object}
 */
export const search2obj = (hash = '') => {
  let ret = {},
    seg = decodeURIComponent(hash).replace(/^\?/, '').split('&'),
    len = seg.length,
    i = 0,
    s;
  for (; i < len; i++) {
    if (!seg[i]) {
      continue;
    }
    s = seg[i].split('=');
    ret[s[0]] = s[1];
  }
  return ret;
};


/**
 * 对象数据扩充
 * @param  {Object} obj 目标对象
 * @param  {Object} ext 扩充对象
 * @return {Object}
 */
export const assign = (obj = {}, ext = {}) => {
  for (let k in ext) {
    if (ext.hasOwnProperty(k)) {
      obj[k] = ext[k];
    }
  }
  return obj;
};


/**
 * 优化 typeof 获取未知对象类型
 * @param  {Mixed} mixed
 * @return {String}       Number|String|Object|Array|Function
 */
export const typeOf = mixed =>
  Object.prototype.toString.apply(mixed).match(/\[object (\w+)\]/)[1];


/**
 * 只是为了读取对象数据避免被覆盖数据用的copy
 */
export const objDataCopy = obj => {
  return JSON.parse(JSON.stringify(obj));
};

/**
 * Cookie操作
 */
export const cookie = {
  /**
   * 设置 cookie
   * @param  {string} name  项
   * @param  {String} value 值
   * @param  {Object} opts  扩展配置
   * @return {null}
   */
  set(name, value = '', options) {
    let data = {},
      cookies = [],
      opts = assign({
        path: '/',
        domain: ''
      }, options);
    if (!name ||
      /^(?:expires|max\-age|path|domain|secure)$/i.test(name)) {
      throw new Error('Cookie名称不能为保留字');
    }
    data[encodeURIComponent(name)] = encodeURIComponent(value);
    if (opts['expires']) {
      throw new Error('使用max-age替换expires');
    }
    for (let opt in opts) {
      data[opt] = opts[opt];
    }
    for (let key in data) {
      cookies.push(`${key}=${data[key]}`);
    }
    cookies = cookies.join('; ');
    document.cookie = `${cookies};`;
  },

  /**
   * 删除cookie某项
   * @param  {string} name
   * @return {null}
   */
  remove(name) {
    this.set(name, '', {
      'max-age': 0
    });
  },

  /**
   * 获取cookie某项
   * @param  {String} key [description]
   * @return {[type]}      [description]
   */
  get(key = '') {
    document.cookie.split('; ').forEach(_cookie => {
      let [name, value] = _cookie.split('=');
      if (key !== '' && key === name) {
        return decodeURIComponent(value);
      }
    });
  }

};

/**
 * 监听模式封装
 */
export const emitter = (el = {}) => {

  /**
   * 所有监听中的回调函数
   * @type {Object}
   */
  let _callbacks = {};

  /**
   * object defineProperty 默认
   * writable : false, configurable : false, enumerable : false
   * 避免被复写
   * 自定义事件
   */
  Object.defineProperty(el, 'on', {
    value(event, fn) {
      if (typeof fn == 'function') {
        (_callbacks[event] = _callbacks[event] || []).push(fn);
      }
      return el;
    }
  });

  Object.defineProperty(el, 'once', {
    value(event, fn) {
      let on = (...args) => {
        el.off(event, on);
        fn.apply(el, args);
      };
      return el.on(event, on);
    }
  });

  /**
   * 解除某自定义事件
   */
  Object.defineProperty(el, 'off', {
    value(event, fn) {
      if (event === '*' && !fn) _callbacks = {};
      else {
        if (fn) {
          for (const _i in _callbacks[event]) {
            if (_callbacks[event][_i] == fn)
              _callbacks[event].splice(_i, 1);
          }
        } else delete _callbacks[event];
      }
      return el;
    }
  });

  /**
   * 触发某自定义事件
   */
  Object.defineProperty(el, 'emit', {
    value(event, ...args) {
      const fns = (_callbacks[event] || []).slice(0);
      fns.forEach(_fn => {
        _fn.apply(el, args);
      });
      if (_callbacks['*'] && event !== '*')
        el.emit.apply(el, ['*', event].concat(args));
      return el;
    }
  });

  return el;

};
